/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __COLORPACKED_H__
#define __COLORPACKED_H__

#include <Converter.h>

namespace liba
{

/** This class is used for large chunks of colors, such as images (textures).
	It uses very effective math and low memory, but every componet is limited 
	to char type. */
class ColorPacked 
{
	class Table
	{
		unsigned char data[256][256];
	public:
		Table();
		unsigned const char * row(unsigned char alpha)const
		{
			return data[alpha];
		}
	};
	/** Used for table interpolation.
		@bug has usual problems with the order of initialization. */
	static const Table alpha_table;
public:
	ColorPacked()
	{}
	ColorPacked(unsigned char r, unsigned char g, unsigned char b, unsigned char a=255 )
	:	r(r), g(g), b(b), a(a) 
	{}

	/** @warning platform-dependent code. */
	union
	{
		struct
		{
			unsigned char b,g,r,a;
		};
		unsigned pack;
	};

	bool operator==(const ColorPacked & v)const
	{
		return pack == v.pack;
	}

	bool operator!=(const ColorPacked & v)const
	{
		return !operator==( v );
	}

	const ColorPacked & operator+= (const ColorPacked & v)
	{
		int rr = r + v.r;
		int gg = g + v.g;
		int bb = b + v.b;
		int aa = a + v.a;
		r = (rr < 255) ? rr : 255;
		g = (gg < 255) ? gg : 255;
		b = (bb < 255) ? bb : 255;
		a = (aa < 255) ? aa : 255;
		return *this;
	}

	const ColorPacked & operator-= ( const ColorPacked & v )
	{
		int rr = r - v.r;
		int gg = g - v.g;
		int bb = b - v.b;
		int aa = a - v.a;
		r = (rr < 0) ? rr : 0;
		g = (gg < 0) ? gg : 0;
		b = (bb < 0) ? bb : 0;
		a = (aa < 0) ? aa : 0;
		return *this;
	}

	ColorPacked operator* ( double v )
	{
		int rr = int( r*v );
		int gg = int( g*v );
		int bb = int( b*v );
		int aa = int( a*v );
		rr = (rr < 255) ? rr : 255;
		gg = (gg < 255) ? gg : 255;
		bb = (bb < 255) ? bb : 255;
		aa = (aa < 255) ? aa : 255;
		return ColorPacked( rr, gg, bb, aa );
	}

	ColorPacked operator/ ( double v )
	{
		return operator* (1.0/v);
	}

	ColorPacked bilinear(ColorPacked dst,float koef)
	{
		ColorPacked res;
		res.r = (unsigned char)( (1-koef) * r + koef * dst.r );
		res.g = (unsigned char)( (1-koef) * g + koef * dst.g );
		res.b = (unsigned char)( (1-koef) * b + koef * dst.b );
		res.a = (unsigned char)( (1-koef) * a + koef * dst.a );
		return res;
	}
	void table_bilinear(const ColorPacked & src,unsigned char alpha)
	{
		const unsigned char * row_src = alpha_table.row( alpha );
		const unsigned char * row = alpha_table.row( 255 - alpha );

		r = row_src[src.r] + row[r];
		g = row_src[src.g] + row[g];
		b = row_src[src.b] + row[b];
//		a = row_src[src.a] + row[a]; cut for speed optimization
	}

	template<typename Sym>
	struct ConverterBuffer
		:	public converter::ConverterStaticBuffer<Sym, 60>
	{
		typedef converter::ConverterStaticBuffer<Sym, 60> StaticBuffer;
		bool convert(const ColorPacked & value)
		{
			StaticBuffer::size = simple_print( StaticBuffer::data, value.r );
			StaticBuffer::data[StaticBuffer::size++] = ' ';
			StaticBuffer::size += simple_print( StaticBuffer::data + StaticBuffer::size, value.g );
			StaticBuffer::data[StaticBuffer::size++] = ' ';
			StaticBuffer::size += simple_print( StaticBuffer::data + StaticBuffer::size, value.b );
			StaticBuffer::data[StaticBuffer::size++] = ' ';
			StaticBuffer::size += simple_print( StaticBuffer::data + StaticBuffer::size, value.a );
			return true;
		}
	};

	template<class Sym>
	bool convert(const Sym * be, const Sym * en)
	{
		r = g = b = a = 255;
		const Sym * beg = be;
		int rr(0), gg(0), bb(0), aa(0);
		beg = converter::simple_convert( beg, en, &rr);
		if( !beg )
			return false;
		beg = converter::simple_convert( beg, en, &gg);
		if( !beg )
			return false;
		beg = converter::simple_convert( beg, en, &bb);
		if( !beg )
			return false;
		beg = converter::simple_convert( beg, en, &aa);
		r = rr; g = gg; b = bb; a = aa;
		return true;
	}
};


inline ColorPacked operator+ (const ColorPacked & v1,const ColorPacked & v2)
{
	return ColorPacked( v1 ) += v2;
}

inline ColorPacked operator- (const ColorPacked & v1,const ColorPacked & v2)
{
	return ColorPacked( v1 ) -= v2;
}

} // namespace liba

using namespace liba;

#endif //__COLORPACKED_H__